package com.example.demo.aspect;

import com.example.demo.annotation.AuthValidate;
import com.example.demo.base.ReqParamBody;
import com.example.demo.base.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义权限注解的切面类
 */
@Aspect
@Component
public class AuthValidatorAdvise {

	private static final Map<Class<?>, Map<String, AuthValidate>> ANNOTATIONS = new ConcurrentHashMap<>();

	/**
	 * 拦截指定注解
	 */
	@Pointcut("@annotation(com.example.demo.annotation.AuthValidate)")
	public void procController() {
	}

	@Around("procController()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		String methodName = joinPoint.getSignature().getName();
		Class<?> classTarget = joinPoint.getTarget().getClass();
		if (!ANNOTATIONS.containsKey(classTarget)) {
			ANNOTATIONS.put(classTarget, new HashMap<String, AuthValidate>());
		}
		if (!ANNOTATIONS.get(classTarget).containsKey(methodName)) {
			Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
			Method objMethod = classTarget.getMethod(methodName, par);
			AuthValidate pv = null;
			if (objMethod.isAnnotationPresent(AuthValidate.class)) {
				pv = objMethod.getAnnotation(AuthValidate.class);
			} else if (classTarget.isAnnotationPresent(AuthValidate.class)) {
				pv = classTarget.getAnnotation(AuthValidate.class);
			}
			ANNOTATIONS.get(classTarget).put(methodName, pv);
		}
		AuthValidate pv = ANNOTATIONS.get(classTarget).get(methodName);
		if (pv == null || (!pv.token())) {
			return joinPoint.proceed();
		} else {
			boolean needToken = pv.token();
			Object[] args = joinPoint.getArgs();
			if (args != null && args.length == 1 && args[0] instanceof ReqParamBody) {
				ReqParamBody<?> reqParam = (ReqParamBody<?>) args[0];
				if (reqParam.getSysCode() == null) {
					return Result.fail("缺少系统编码");
				}
				if (needToken) {
					if (reqParam.getToken() == null) {
						return Result.fail("403","缺少访问令牌");
					}
					// 校验访问令牌
					boolean bFlag = this.validate(reqParam.getToken());
					if (!bFlag) {
						return Result.fail("403","访问令牌失效");
					}
				}
				return joinPoint.proceed();
			} else {
				return Result.fail("参数不符合格式");
			}
		}
	}

	/**
	 * 校验token
	 * @param token 访问令牌
	 * @return
	 */
	private boolean validate(String token) {

		//注意：为了提高接口效率，token会存放在Redis，此处token只是作为获取Redis上真正token值的key
		//一般流程：
		// 1. 根据token获取Redis上的token值和有效时间
		// 2. 检查token有效时间
		// 3. 检查token准确性
		// 4. token时间有效并且准确返回true，否则返回false

		if("success_token".equals(token)){
			return true;
		}

		return false;
	}

}
